﻿#include "util.h"

#include "log.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <functional>
#include "const.h"
#include <sys/stat.h>



// 字符串分割函数
size_t Util::split(const std::string &src, const std::string &sep, std::vector<std::string> *ans, bool _reserve_blank)
{
     // ab,bc,cd,de
    size_t left = 0;
    size_t right = src.find(sep, left);
    while (right != std::string::npos)
    {
        auto tmp = src.substr(left, right - left);
        if (tmp.size() != 0 || _reserve_blank)
        {
            ans->push_back(tmp);
        }
        left = right + 1;
        right = src.find(sep, left);
    }
    if (left != src.size())
    {
        ans->push_back(src.substr(left));
    }
    return ans->size();
}

// 读取文件内容
bool Util::readFile(const std::string &filename, Buffer *buf)
{
    std::ifstream rd(filename, std::ios::binary);
    if (rd.is_open() == false)
    {
        LOG(Warning, "open %s file fail!!", filename.c_str());
        return false;
    }
    size_t fsize = 0;
    rd.seekg(0, rd.end);
    fsize = rd.tellg();
    rd.seekg(0, rd.beg);
    std::string str;
    str.resize(fsize);
    rd.read(&str[0], fsize);
    if (rd.good() == false)
    {
        LOG(Warning, "Read %s file fail!!", filename.c_str());
        rd.close();
        return false;
    }
    rd.close();
    buf->writeString(str);
    return true;
}
// 写入文件
bool Util::writeFile(const std::string &filename, const std::string &src)
{
    std::ofstream wt(filename, std::ios::binary | std::ios::trunc);
    if (wt.is_open() == false)
    {
        LOG(Warning, "open %s file fail!!", filename.c_str());
        return false;
    }
    wt.write(src.c_str(), src.size());
    if (wt.good() == false)
    {
        LOG(Warning, "write %s file fail!!", filename.c_str());
        wt.close();
        return false;
    }
    wt.close();
    return true;
}
// URL编码，避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
// 编码格式：将特殊字符的ascii值，转换为两个16进制字符，前缀%   C++ -> C%2B%2B
// 不编码的特殊字符： RFC3986文档规定 . - _ ~ 字母，数字属于绝对不编码字符
// RFC3986文档规定，编码格式 %HH
// W3C标准中规定，查询字符串中的空格，需要编码为+， 解码则是+转空格
std::string Util::urlEncode(const std::string &url, bool space_to_plus)
{
    std::string ret;
    for (char c : url)
    {
        // 保留不编码的字符
        if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
        {
            ret += c;
            continue;
        }
        // 对于空格，使用加号 (+) 编码
        if (c == ' ' && space_to_plus)
        {
            ret += '+';
            continue;
        }
        // 对于其他字符，使用百分比编码
        char tmp[4];
        snprintf(tmp, 4, "%%%02X", c);
        ret += tmp;
    }
    return ret;
}
// URL解码
std::string Util::urlDecode(const std::string &url, bool plus_to_space)
{
    std::function<int(char)> HeToDe = [](char c) -> char
    {
        if (c >= '0' && c <= '9')
        {
            return c - '0';
        }
        else if (c >= 'a' && c <= 'z')
        {
            return c - 'a' + 10;
        }
        else if (c >= 'A' && c <= 'Z')
        {
            return c - 'A' + 10;
        }
        else
        {
            LOG(Fatal, "unknow");
            abort();
        }
    };
    std::string ret;
    for (int i = 0; i < url.size(); i++)
    {
        if (url[i] == '+' && plus_to_space)
        {
            ret += ' ';
            continue;
        }
        if (url[i] == '%')
        {
            char v1 = HeToDe(url[i + 1]);
            char v2 = HeToDe(url[i + 2]);
            char v = (v1 << 4) + v2;
            ret += v;
            i += 2;
            continue;
        }
        ret += url[i];
    }
    return ret;
}
// 获取响应状态码的描述信息
std::string Util::statuDes(int statucode)
{
    auto it = g_statuMsg.find(statucode);
    if (it != g_statuMsg.end())
    {
        return it->second;
    }
    return "unknow";
}
// 根据文件后缀名获取文件mime
std::string Util::mimeDes(const std::string &filename)
{
    size_t pos = filename.find_last_of('.');
    if (pos == std::string::npos)
    {
        return "application/octet-stream";
    }
    auto it = g_mimeMsg.find(filename.substr(pos));
    if (it == g_mimeMsg.end())
    {
        return "application/octet-stream";
    }
    return it->second;
}
// 判断是否是目录
bool Util::isDirectory(const std::string &filename)
{
    struct stat st;
    int ret = stat(filename.c_str(), &st);
    if (ret < 0)
    {
        return false;
    }
    return S_ISDIR(st.st_mode);
} 
// 判断是否是普通文件
bool Util::isRegular(const std::string &filename)
{
    struct stat st;
    int ret = stat(filename.c_str(), &st);
    if (ret < 0)
    {
        return false;
    }
    return S_ISREG(st.st_mode);
}
// 判断路径是否有效
bool Util::validPath(const std::string &path)
{
    std::vector<std::string> subdir;
    split(path, "/", &subdir);
    int depth = 0;
    for (auto &e : subdir)
    {
        if (e == "..")
        {
            depth--;
            if (depth < 0)
            {
                return false;
            }
        }
        else
        {
            depth++;
        }
    }
    return true;
}